package org.dmcs.homework.statistics.implementations;

import java.util.Arrays;

import org.dmcs.homework.statistics.api.ICovarianceCalculator;
import org.dmcs.homework.statistics.api.IMeanCalculator;

import Jama.Matrix;

/**
 * Calculates the covariance matrix for a given data set and a given class of objects.
 * @author Zielony
 * @version 1.0
 */
public class CovarianceCalculator implements ICovarianceCalculator {
	/**
	 * The calculator for the mean vector.
	 */
	IMeanCalculator meanCalculator;
	
	/**
	 * Creates the instance using the given <code>meanCalculator</code>.
	 * @param meanCalculator the tool for calculating mean vectors.
	 */
	public CovarianceCalculator(IMeanCalculator meanCalculator) {
		this.meanCalculator = meanCalculator;
	}
	
	/**
	 * {@inheritDoc}
	 */
	public Matrix calculateCovarianceMatrix(Matrix data, int[] classIndices, 
			int[] selectedFeatures) {
		/*
		 * Obliczanie wektora sredniej.
		 */
		double[] meanVector = meanCalculator.calculateMeanVector(data, classIndices);
		
		/*
		 * Wektor sredniej w postaci macierzowej oraz po selekcji cech.
		 */
		Matrix meanVectorMatrix = new Matrix(selectedFeatures.length, 1);
		
		
		
		/*
		 * Wybór cech do wektora sredniej zgodnie z wejsciem.
		 */
		for(int i=0; i<selectedFeatures.length; i++) {
			meanVectorMatrix.set(i, 0, meanVector[selectedFeatures[i]]);
		}
		
		/*
		 * Macierz - wektor reprezentujcy i-ty obiekt klasy.
		 */
		Matrix iObject;
		
		/*
		 * Różnica miedzy i-tym obiektem a srednia dla klasy. 
		 */
		Matrix iDifferenceMatrix;
		
		/*
		 * Wynikowa macierz kowariancji.
		 */
		Matrix covarianceMatrix = new Matrix(selectedFeatures.length, selectedFeatures.length);
		
		for(int j=0; j<classIndices.length; j++) {
			/*
			 * Tworzenie wektora dla j-tego obiektu.
			 */
			iObject = new Matrix(selectedFeatures.length, 1);
			
			for(int i=0; i<selectedFeatures.length; i++) {
				/*
				 * Pobieranie danych do każdego z pól wektora.
				 */
				iObject.set(i, 0, data.get(selectedFeatures[i], classIndices[j]));
			}
			
			/*
			 * Obliczanie różnicy miedzy srednia a i-tym obiektem
			 */
			iDifferenceMatrix = iObject.minus(meanVectorMatrix);
			
			/*
			 * Dodawanie wyniku do kowariancji.
			 */
			covarianceMatrix = covarianceMatrix.plus(iDifferenceMatrix.times(iDifferenceMatrix.transpose()));
		}
		
		return covarianceMatrix;
	}

	public Matrix calculateCovarianceMatrix(Matrix data, int[] classIndices) {
		//TODO: Obsluga wyjatkow na wejsciu.
		/*
		 * Wybieranie wszystkich cech na raz.
		 */
		int[] selectedFeatures = new int[data.getRowDimension()];
		for(int i=0; i<selectedFeatures.length; i++) {
			selectedFeatures[i] = i;
		}
		
		/*
		 * Korzystanie z gotowego mechanizmu obliczania kowariancji dla podanych
		 *  wymiarów.
		 */
		return this.calculateCovarianceMatrix(data, classIndices, selectedFeatures);
	}
}